using System;
using System.Collections.Generic;
using System.Text;

using OhioState.Graphics;


using Tao.OpenGl;

namespace OhioState.Graphics.OpenGL
{
    public class ShaderProgramGL : IShaderProgram, IOpenGLResource, IDisposable
    {
        public ShaderProgramGL()
        {
            // Do not make OpenGL calls here
        }

        #region IShader Members
        public void MakeActive(IRenderPanel panel)
        {
            if (!created)
            {
                Create();
            }
            if (needsLinked)
            {
                if (!Link())
                    return;
                needsLinked = false;
            }
            Gl.glUseProgram(guid);
        }

        public void Deactivate()
        {
            //disable the program object
            Gl.glUseProgram(0);
        }
        #endregion

        public void AttachShader(IShaderRoutine shader)
        {
            if (!created)
            {
                Create();
            }
            if (!shaderList.Contains(shader))
            {
                shaderList.Add(shader);
                Gl.glAttachShader(guid, (shader as IOpenGLResource).GUID);
                needsLinked = true;
            }
        }

        public void DetachShader(IShaderRoutine shader)
        {
            if (created)
            {
                if (!shaderList.Contains(shader))
                {
                    shaderList.Remove(shader);
                    Gl.glDetachShader(guid, (shader as IOpenGLResource).GUID);
                    needsLinked = true;
                }
            }
        }

        public void RemoveAllRoutines()
        {
            if (!created)
                return;

            foreach (IShaderRoutine shader in shaderList)
            {
                Gl.glDetachShader(guid, (shader as IOpenGLResource).GUID);
            }
            shaderList.Clear();
            needsLinked = true;
        }

        public bool LinkStatus
        {
            get { return linkStatus; }
        }

        public string LinkLog
        {
            get { return _linkLog.ToString(); }
        }

        // step 6: Link() will link the registered shader routines into a GLSL Program.
        //         It will also determine the uniform variable locations and provide a mapping
        //         from the name space to the int location. 
        public bool Link()
        {
            int linkInfo;
            int maxLength;

            Gl.glLinkProgram(guid);
            //
            // The status of the link operation will be stored as part of the program object's state. 
            // This value will be set to GL_TRUE if the program object was linked without errors and 
            // is ready for use, and GL_FALSE otherwise. It can be queried by calling glGetProgramiv 
            // with arguments program and GL_LINK_STATUS. 
            //
            Gl.glGetProgramiv(guid, Gl.GL_LINK_STATUS, out linkInfo);
            linkStatus = (linkInfo == Gl.GL_TRUE);

            Gl.glGetProgramiv(guid, Gl.GL_INFO_LOG_LENGTH, out maxLength);
            _linkLog.EnsureCapacity(maxLength);
            Gl.glGetProgramInfoLog(guid, maxLength, out maxLength, _linkLog);

            return linkStatus;
        }

        #region IOpenGLResource Members
        public uint GUID
        {
            get { return guid; }
        }
        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
        }
        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                this.RemoveAllRoutines();
            }
            if (created)
            {
                Gl.glDeleteProgram(guid);
            }
            GC.SuppressFinalize(this);
        }

        #endregion

        #region Implementation
        private void Create()
        {
            unchecked
            {
                guid = (uint)Gl.glCreateProgram();
            }
            created = true;
        }
        #endregion

        #region Member variables
        private bool needsLinked = false;
        private uint guid = 0;
        private bool linkStatus = false;
        private StringBuilder _linkLog = new StringBuilder(1024);
        private bool created = false;
        private List<IShaderRoutine> shaderList = new List<IShaderRoutine>();
        #endregion
    }
}
